Bug fixes 4 6 2026#4
Merged
Merged
Conversation
…n on map Root Cause fetchLatest() had 5 early-return paths that didn't refresh the Redis TTL for existing scans: 1. S3 listing returns HTTP error 2. S3 listing has no contents (happens at UTC date rollover) 3. No V06/V08 volume files found 4. File timestamp parse fails 5. Station data >30 min old (stale) ← the most common trigger Since SCAN_TTL and MAX_AGE_MS are both 30 minutes, once a station's S3 data ages past 30 minutes the TTL stops being refreshed, and the scan expires from Redis within the remaining TTL window. At z8+ there's no MRMS fallback (tilers produce z2-7 only), so expired scans = transparent tiles. The previous fix (commit 4e01caf) increased TTL from 10 to 30 minutes — this delayed the symptom from ~10 min to ~30 min but didn't address the root cause. Changes src/nexrad/main.ts — Unconditionally refresh TTL for previously-ingested stations at the top of fetchLatest(), before any S3 work. This single line covers all failure paths (stale data, S3 errors, network issues, date rollover). The .catch(() => {}) prevents Redis connection errors from blocking the fetch attempt. src/server/nexrad-scan-provider.ts — Wrap readScanFromRedis in try/catch so Redis connection blips don't crash tile requests (defense-in-depth).
Two changes across both public/index.html (the app) and landing/index.html (the homepage): 1. Replaced 375K Leaflet calls per frame with inline math The old code called map.latLngToContainerPoint() for every trail point of every particle — 15,000 particles × 25 trail points = 375,000 calls per frame. Each call created 3 Leaflet objects and did full Mercator projection math, generating ~1.1M garbage objects per frame for the GC. The fix stores trails in normalized Mercator coordinates (nx = lon/360 + 0.5, ny via a single log/tan), extracts the map transform once per frame with a single Leaflet call, then converts all trail points with just x = nx * scale + ox (multiply + add). Same visual result, ~100x less work. 2. Batched canvas draw calls by color The old code did a separate beginPath() / stroke() for each particle — 15,000 stroke() calls per frame. Each stroke() triggers rasterization. The fix bins particles by their color index, then draws each color group with a single beginPath() / stroke(). Reduces draw calls from ~15K to ~20. 3. Removed the pause/resume mechanism The old code froze the animation on movestart/zoomstart and resumed on moveend/zoomend. This caused the visible "freeze then recalculate" behavior. With the inline math being fast enough (~2-4ms per frame vs the old ~50ms), the animation now runs continuously during pan/zoom with no pause needed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1
Closes #2
Changes
latLngToContainerPointcalls with inlineMercator math (1 Leaflet call + multiply/add per trail point), batch canvas
stroke()calls by color (15K → ~20), removepause/resume mechanism so wind flows continuously during interaction
work — prevents scans from expiring through stale data, S3 errors, date rollover, or network failures
getScanso connection blips don'tcrash tile requests
Testing
npx tsc --noEmitpassesdocker compose upScreenshots
N/A — no visual design changes, only performance and reliability fixes.